use std::{borrow::Cow, sync::{atomic::{AtomicBool, Ordering}, Arc}, time::Duration};

use tauri::Window;
use tokio::{io::{AsyncReadExt, AsyncWriteExt, BufReader}, select, sync::Semaphore, time::timeout};
use tokio_serial::{new, SerialPortBuilderExt, SerialStream};

use crate::{error::{AppErr, ErrConv, IoErr}, service::frame::{HEAD0, HEAD1}, utils::{as_mut, new_buf}};

use super::{frame::{sum_xor, END}, RECV_EVENT};


unsafe impl Send for SerialPort {}
unsafe impl Sync for SerialPort {}

pub struct SerialPort {

    window: Window,
    stream: BufReader<SerialStream>,
    exit_sem: Semaphore,
    exit_flag: AtomicBool,
}

pub type SharedPort = Arc<SerialPort>;

impl SerialPort {

    pub fn new(window: Window, name: &str) -> Result<SharedPort, AppErr> {
        let stream = new(name, 115200)
            .open_native_async()
            .conv()?;

        let sp = SerialPort {
            window,
            stream: BufReader::with_capacity(256, stream),
            exit_sem: Semaphore::new(0),
            exit_flag: AtomicBool::new(false),
        };

        let sp = Arc::new(sp);

        tokio::spawn( read_task(sp.clone()) );

        Ok(sp)
    }

    pub fn close(&self) {
        _ = self.exit_sem.add_permits(1);
    }

    async fn read(&self, buf: &mut[u8]) -> Result<usize, AppErr> {
        let n = as_mut(&self.stream).read(buf).await?;
        if n <= 0 {
            return Err(AppErr::Closed);
        }
        Ok(n)
    }

    pub async fn write(&self, buf: &[u8]) -> Result<(), AppErr> {
        if self.exit_flag.load(Ordering::Acquire) {
            return Err(AppErr::Closed);
        }
        as_mut(&self.stream).write_all(buf).await?;
        Ok(())
    }

    async fn read_all(&self, buf: &mut[u8]) -> Result<(), AppErr> {
        let len = buf.len();
        let mut index = 0;
        while index < len {
            let n = self.read(&mut buf[index..]).await?;
            index += n;
        }
        Ok(())
    }

    async fn read_frame(&self) -> Result<Box<[u8]>, AppErr> {
        let mut head = [0u8; 1];
        let mut flag = false;
        loop {
            self.read(&mut head).await?;
            if flag && (head[0] == HEAD1) {
                break;
            }
            flag = head[0] == HEAD0;
        }
        self.read(&mut head).await?;
        let len = head[0] as usize;
        if len < 7 {
            return Err(AppErr::Proto(Cow::Borrowed("invalid: len < 7")));
        }

        let mut buf = new_buf(len - 3);
        timeout(Duration::from_millis(500), self.read_all(&mut buf)).await
        .map_err(|_| AppErr::Proto(Cow::Borrowed("recv timeout")))??;

        let s = sum_xor(&buf[..(len - 3 - 2)]);
        if s != buf[len - 3 - 2] {
            return Err(AppErr::Proto(Cow::Borrowed("invalid sum")));
        }

        if END != buf[len - 3 - 1] {
            return Err(AppErr::Proto(Cow::Borrowed("invalid end")));
        }

        Ok(buf)
    }

    async fn read_loop(&self) -> Result<(), AppErr> {

        loop {
            let frame = self.read_frame().await?;
            _ = self.window.emit(RECV_EVENT, frame);
        }
    }
}

async fn read_task(sp: SharedPort) {

    loop {
        let ret = select! {
            _ = sp.exit_sem.acquire() => {
                break;
            }

            ret = sp.read_loop() => {
                ret
            }
        };

        match ret {
            Ok(_) => {}
            Err(AppErr::Proto(e)) => {
                println!("err:{}", e);
            }
            Err(e) => {
                println!("err:{}", e);
                break;
            }
        };
    }
    sp.exit_flag.store(true, Ordering::SeqCst);
    println!("read exit");
}